/**
 * @file 密码自动填充预加载脚本
 * @description 在外部网站中检测登录表单并提供自动填充功能
 * @author AI Assistant
 * @version 1.0.0
 * @since 2025-10-22
 */
import { PASSWORD_MANAGER_CHANNELS } from '~shared/ipc-topic'

const { ipcRenderer } = require('electron') as typeof import('electron')

/**
 * 登录表单信息
 */
interface LoginForm {
  form: HTMLFormElement | null
  usernameField: HTMLInputElement | null
  passwordField: HTMLInputElement | null
  submitButton: HTMLButtonElement | HTMLInputElement | null
}

/**
 * 凭据信息
 */
interface Credential {
  _id: string
  domain: string
  site_name?: string
  username: string
  password: string
  favicon_url?: string
  auto_fill_enabled: boolean
}

/**
 * 密码自动填充管理器
 */
export class PasswordAutoFillManager {
  private observer: MutationObserver | null = null
  private isInitialized = false

  // —— 非 form 场景支持所需的状态 ——

  private lastUsernameField: HTMLInputElement | null = null

  private lastPasswordField: HTMLInputElement | null = null
  private lastPwAt = 0
  private lastSavePromptAt = 0
  private networkPatched = false

  /**
   * 初始化自动填充管理器
   */
  async init(): Promise<void> {
    if (this.isInitialized) return
    this.isInitialized = true

    try {
      // 检查密码管理器是否启用
      const isPasswordManagerEnabled = await this.checkPasswordManagerEnabled()

      if (!isPasswordManagerEnabled) {
        return
      }
      // 优先打补丁网络钩子（覆盖无 FORM 提交/按钮触发/SPA 请求）
      this.patchNetworkHooks()
      // 监听DOM变化
      this.setupMutationObserver()

      // 扫描现有表单
      this.scanForLoginForms()

      // 监听表单提交事件，用于保存密码
      this.setupFormSubmissionListener()

      // 监听来自子 iframe 的“请求填充”消息
      this.setupFrameFillRequestListener()

      // 监听全局输入/焦点（覆盖无表单/多步登录等）
      this.setupGlobalInteractionListeners()
    } catch (error) {
      console.error('🔐 密码自动填充管理器初始化失败:', error)
    }
  }

  /**
   * 检查密码管理器是否启用
   */
  private async checkPasswordManagerEnabled(): Promise<boolean> {
    try {
      const result = await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.GET_SETTINGS)

      return result.success && result.data?.enabled
    } catch (error) {
      console.error('检查密码管理器状态失败:', error)
      return false
    }
  }

  /**
   * 设置DOM变化监听器
   */
  private setupMutationObserver(): void {
    this.observer = new MutationObserver(mutations => {
      let shouldScan = false

      for (const mutation of mutations) {
        const target = mutation.target as Element

        if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
          for (const node of mutation.addedNodes) {
            if (node.nodeType === Node.ELEMENT_NODE) {
              const el = node as Element
              // 只要出现 FORM 或 新增了密码/用户名输入框，就进行一次扫描
              if (
                el.tagName === 'FORM' ||
                el.querySelector('form') ||
                el.matches?.(
                  'input[type="password"], input[autocomplete="username"], input[autocomplete="current-password"], input[autocomplete="new-password"], input[placeholder*="密码"], input[name*="pwd"], input[id*="pwd"]'
                ) ||
                el.querySelector?.(
                  'input[type="password"], input[autocomplete="username"], input[autocomplete="current-password"], input[autocomplete="new-password"], input[placeholder*="密码"], input[name*="pwd"], input[id*="pwd"]'
                )
              ) {
                shouldScan = true
              }
            }
          }
        }
        // form显示时
        if (mutation.type === 'attributes' && target.tagName === 'IFRAME') {
          shouldScan = true
        }
        if (target.tagName === 'FORM' && mutation.attributeName === 'style') {
          shouldScan = true
        }
      }

      if (shouldScan) setTimeout(() => this.scanForLoginForms(), 100)
    })

    this.observer.observe(document.body, { childList: true, subtree: true, attributes: true })
  }

  /**
   * 扫描页面中的登录表单
   */
  private scanForLoginForms(): void {
    const forms = this.getAllFormsDeep()

    for (const form of forms) {
      const loginForm = this.analyzeForm(form)
      if (loginForm.usernameField && loginForm.passwordField) {
        this.setupAutoFill(loginForm)
      }
    }
    // 额外：支持无 FORM 的页面，尝试配对附近的用户名/密码输入框
    this.scanLooseLoginFields()
  }

  private scanLooseLoginFields(): void {
    try {
      const allInputs = Array.from(document.querySelectorAll('input')) as HTMLInputElement[]
      const pwFields = allInputs.filter(inp => this.isPwInput(inp) && this.isUsableInput(inp))
      pwFields.forEach(pw => {
        const usernameField = this.findNearestUsernameField(pw)
        if (this.isUsableInput(usernameField)) {
          this.setupAutoFill({
            form: pw.form || null,
            usernameField: usernameField!,
            passwordField: pw,
            submitButton: null
          })
        }
      })
    } catch {
      /* ignore */
    }
  }

  // 遍历主文档、开放的 Shadow DOM 与同源 iframe，收集所有表单
  private getAllFormsDeep(rootDoc: Document = document): HTMLFormElement[] {
    try {
      const forms = rootDoc.querySelectorAll?.('form') as NodeListOf<HTMLFormElement> | undefined
      return forms ? Array.from(forms) : []
    } catch {
      return []
    }
  }

  private isPwInput(inp: HTMLInputElement): boolean {
    if (!inp || inp.disabled || inp.readOnly || inp.type === 'hidden') return false
    const type = (inp.type || '').toLowerCase()
    const name = (inp.name || '').toLowerCase()
    const id = (inp.id || '').toLowerCase()
    const placeholder = (inp.placeholder || '').toLowerCase()
    const ac = (inp.autocomplete || '').toLowerCase()
    const idNamePlaceHint = `${name} ${id} ${placeholder}`
    const otpKeywords = ['code', '验证码', '驗證碼', 'captcha', 'otp', '一次性', '动态', '動態']
    if (otpKeywords.some(k => idNamePlaceHint.includes(k))) return false

    const passKeywords = ['pass', 'pwd', 'password', '密碼', '密码']
    const isTypeMatch = type === 'password'
    const isAutoCompleteMatch =
      ac === 'current-password' || ac === 'new-password' || ac === 'password'
    const isKeywordMatch = passKeywords.some(k => idNamePlaceHint.includes(k))
    const isTextButLooksLikePw = type === 'text' && (isKeywordMatch || id === 'password')
    return isTypeMatch || isAutoCompleteMatch || isTextButLooksLikePw
  }

  /**
   * 分析表单结构
   */
  private analyzeForm(form: HTMLFormElement, isUsable?: boolean): LoginForm {
    const inputs = Array.from(form.querySelectorAll('input')) as HTMLInputElement[]

    let usernameField: HTMLInputElement | null = null
    // 查找密码字段
    let passwordField: HTMLInputElement | null = inputs.find(input => this.isPwInput(input)) || null
    if (isUsable) {
      passwordField =
        inputs.find(input => this.isPwInput(input) && this.isUsableInput(input)) || null
    }
    let submitButton: HTMLButtonElement | HTMLInputElement | null

    // 查找用户名字段
    if (passwordField) {
      const passwordIndex = inputs.indexOf(passwordField)

      // 在密码字段之前查找用户名字段（可选地过滤不可用输入）
      for (let i = passwordIndex - 1; i >= 0; i--) {
        const input = inputs[i]
        if (!input) continue
        if (isUsable && !this.isUsableInput(input)) continue
        if (this.isUsernameField(input)) {
          usernameField = input
          break
        }
      }

      // 如果没找到，在整个表单中查找（可选地过滤不可用输入）
      if (!usernameField) {
        usernameField =
          inputs.find(input =>
            isUsable && !this.isUsableInput(input) ? false : this.isUsernameField(input)
          ) || null
      }
    }

    // 查找提交按钮
    submitButton = form.querySelector('button[type="submit"], input[type="submit"]') as
      | HTMLButtonElement
      | HTMLInputElement
    if (!submitButton) {
      submitButton = form.querySelector(
        'button:not([type]), button[type="button"]'
      ) as HTMLButtonElement
    }

    return {
      form,
      usernameField,
      passwordField,
      submitButton
    }
  }

  /**
   * 判断是否为用户名字段
   */
  private isUsernameField(input: HTMLInputElement): boolean {
    if (!input || input.disabled || input.readOnly || input.type === 'hidden') return false

    const type = (input.type || '').toLowerCase()
    const name = (input.name || '').toLowerCase()
    const id = (input.id || '').toLowerCase()
    const placeholder = (input.placeholder || '').toLowerCase()
    const autocomplete = (input.autocomplete || '').toLowerCase()
    const ariaLabel = (input.getAttribute('aria-label') || '').toLowerCase()
    const classes = (input.className || '').toLowerCase()

    const fieldIdentifiers = [name, id, placeholder, autocomplete, ariaLabel, classes].join(' ')

    // 排除验证码/一次性代码等字段
    const otpKeywords = ['code', '验证码', '驗證碼', 'captcha', 'otp', '一次性', '动态', '動態']
    if (otpKeywords.some(k => fieldIdentifiers.includes(k))) return false

    // 明确标注了 username 的优先
    if (autocomplete === 'username') return true

    // 支持更宽泛的用户名输入类型与关键词（中英文）
    const allowedTypes = ['email', 'text', 'tel', 'number']
    if (!allowedTypes.includes(type)) return false

    const usernameKeywords = [
      'user',
      'login',
      'email',
      'account',
      'username',
      'phone',
      'mobile',
      'tel',
      '账号',
      '帳號',
      '帐号',
      '账户',
      '用户名',
      '用戶名',
      '手机',
      '手机号',
      '手機',
      '邮箱',
      '郵箱'
    ]

    // 识别“看起来像邮箱/手机号”的占位提示
    const looksLikeEmailOrPhone = placeholder.includes('@') || /\d{3,}/.test(placeholder)

    return usernameKeywords.some(k => fieldIdentifiers.includes(k)) || looksLikeEmailOrPhone
  }

  /**
   * 设置自动填充
   */
  private async setupAutoFill(loginForm: LoginForm): Promise<void> {
    if (!loginForm.usernameField || !loginForm.passwordField) return

    const domain = window.location.hostname

    try {
      const result = await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.GET_SETTINGS)

      const enabled = result.success && result.data?.auto_fill_enabled
      if (!enabled) return
      const credentials = await this.getCredentialsForDomain(domain)
      if (credentials.length === 0) {
        return
      }

      // 创建自动填充提示
      this.createAutoFillPrompt(loginForm, credentials)
    } catch (error) {
      console.error('获取凭据失败:', error)
    }
  }

  /**
   * 获取指定域名的凭据（仅启用自动填充的）
   */
  private async getCredentialsForDomain(domain: string): Promise<Credential[]> {
    try {
      const result = await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.GET_DECRYPTED_CREDENTIALS, {
        domain,
        auto_fill_enabled: true,
        sortBy: 'last_used_at',
        sortOrder: 'desc'
      })

      return result.success && result.data ? result.data : []
    } catch (error) {
      console.error('获取凭据失败:', error)
      return []
    }
  }

  /**
   * 获取指定域名的所有凭据（不限制是否启用自动填充）
   */
  private async getAllCredentialsForDomain(domain: string): Promise<Credential[]> {
    try {
      const result = await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.GET_DECRYPTED_CREDENTIALS, {
        domain,
        sortBy: 'last_used_at',
        sortOrder: 'desc'
      })

      return result.success && result.data ? result.data : []
    } catch (error) {
      console.error('获取所有凭据失败:', error)
      return []
    }
  }

  /**
   * 创建“输入框下拉选择”样式的自动填充（默认填充第一个，focus 显示，blur 隐藏，支持键盘与实时过滤）
   */
  private createAutoFillPrompt(loginForm: LoginForm, credentials: Credential[]): void {
    if (!loginForm.usernameField || credentials.length === 0) return

    const input = loginForm.usernameField
    const pass = loginForm.passwordField
    let anchorEl: HTMLInputElement = input

    // 避免为同一个输入框重复绑定
    if ((input as any)._pafBound) return
    ;(input as any)._pafBound = true

    // 1) 默认填充第一个
    const defaultCred = credentials[0]
    if (defaultCred) this.fillCredentials(loginForm, defaultCred).catch(() => {})

    // 状态：当前过滤后的列表与选中下标
    let filtered: Credential[] = [...credentials]
    let selectedIndex = -1

    // 2) 创建下拉容器（固定定位，宽度与输入框一致）
    const doc = input.ownerDocument
    const view = doc.defaultView || window
    const dropdown = doc.createElement('div')
    dropdown.className = 'password-autofill-dropdown'
    dropdown.style.cssText = `
      position: fixed;
      background: #fff;
      border: 1px solid #ddd;
      border-radius: 6px;
      box-shadow: 0 8px 20px rgba(0,0,0,0.12);
      z-index: 999999;
      padding: 4px 0;
      display: none;
      max-height: 240px;
      overflow: auto;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      font-size: 14px;
    `

    const position = () => {
      const rect = anchorEl.getBoundingClientRect()
      dropdown.style.left = `${Math.max(0, Math.min(rect.left, (view as Window).innerWidth - rect.width))}px`
      dropdown.style.top = `${rect.bottom + 2}px`
      dropdown.style.width = `${rect.width}px`
    }

    const show = (anchor?: HTMLInputElement | null) => {
      if (anchor) anchorEl = anchor
      position()
      dropdown.style.display = 'block'
    }
    const hide = () => {
      dropdown.style.display = 'none'
    }

    const clearChildren = (el: HTMLElement) => {
      while (el.firstChild) el.firstChild.remove()
    }

    const commitSelection = (idx: number) => {
      const cred = filtered[idx]
      if (!cred) return
      this.fillCredentials(loginForm, cred)
      hide()
    }

    const updateActive = () => {
      const items = Array.from(dropdown.querySelectorAll<HTMLElement>('.password-autofill-option'))
      for (const [i, el] of items.entries()) {
        el.style.background = i === selectedIndex ? '#e8f0fe' : 'transparent'
      }
      const active = items[selectedIndex]
      if (active) {
        const aTop = active.offsetTop,
          aBot = aTop + active.offsetHeight
        if (aTop < dropdown.scrollTop) dropdown.scrollTop = aTop
        else if (aBot > dropdown.scrollTop + dropdown.clientHeight)
          dropdown.scrollTop = aBot - dropdown.clientHeight
      }
    }

    const render = () => {
      clearChildren(dropdown)
      for (const [idx, cred] of filtered.entries()) {
        const item = doc.createElement('div')
        item.className = 'password-autofill-option'
        item.style.cssText =
          'padding:8px 12px;cursor:pointer;display:flex;flex-direction:column;gap:2px;'

        const userLine = doc.createElement('div')
        userLine.style.cssText =
          'font-weight:500;color:#333;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;'
        userLine.textContent = cred.username
        item.append(userLine)

        if (cred.site_name) {
          const siteLine = doc.createElement('div')
          siteLine.style.cssText =
            'font-size:12px;color:#666;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;'
          siteLine.textContent = cred.site_name
          item.append(siteLine)
        }

        item.addEventListener('mouseenter', () => {
          selectedIndex = idx
          updateActive()
        })
        item.addEventListener('mouseleave', () => {
          /* 保持当前选中，避免闪烁 */
        })
        item.addEventListener('pointerdown', e => {
          e.preventDefault()
          commitSelection(idx)
        })

        dropdown.append(item)
      }
      updateActive()
    }

    const filterByInput = () => {
      const q = input.value.trim().toLowerCase()
      filtered = q
        ? credentials.filter(
            c =>
              c.username.toLowerCase().includes(q) || (c.site_name || '').toLowerCase().includes(q)
          )
        : [...credentials]
      selectedIndex = filtered.length > 0 ? 0 : -1
      render()
      if (filtered.length > 0) show()
      else hide()
    }

    // 初始渲染
    render()

    // 4) 绑定 input 事件：focus 显示全部，blur 隐藏，输入实时过滤
    input.addEventListener('click', () => {
      const dd = doc.querySelector('.password-autofill-dropdown') as HTMLDivElement
      if (dd && dd.style.display === 'none') {
        filtered = [...credentials]
        selectedIndex = filtered.length > 0 ? 0 : -1
        render()
        show()
      }
    })
    input.addEventListener('blur', () => setTimeout(() => hide(), 0))
    input.addEventListener('input', filterByInput)
    // 追加：支持 focus 打开；密码框聚焦时也显示多账号选择
    const openDropdownFor = (el: HTMLInputElement) => {
      filtered = [...credentials]
      selectedIndex = filtered.length > 0 ? 0 : -1
      render()
      if (filtered.length > 0) show(el)
    }

    // 用户名框获得焦点时显示
    input.addEventListener('focus', () => openDropdownFor(input))

    // 密码框获得焦点/点击时显示（仅在多账号时弹出）
    if (pass) {
      pass.addEventListener('focus', () => {
        if (credentials.length > 1) openDropdownFor(pass)
      })
      pass.addEventListener('click', () => {
        if (credentials.length > 1) openDropdownFor(pass)
      })
      pass.addEventListener('blur', () => setTimeout(() => hide(), 0))
      // 密码框键盘交互与用户名框一致
      pass.addEventListener('keydown', (e: KeyboardEvent) => {
        if (dropdown.style.display === 'none') return
        if (['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(e.key)) e.preventDefault()
        switch (e.key) {
          case 'ArrowDown': {
            if (filtered.length === 0) return
            selectedIndex = selectedIndex < filtered.length - 1 ? selectedIndex + 1 : 0
            updateActive()
            break
          }
          case 'ArrowUp': {
            if (filtered.length === 0) return
            selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : filtered.length - 1
            updateActive()
            break
          }
          case 'Enter': {
            if (selectedIndex >= 0) commitSelection(selectedIndex)
            break
          }
          case 'Escape': {
            hide()
            break
          }
        }
      })
    }

    // 5) 键盘交互：上下键选择，Enter 确认，Esc 隐藏
    input.addEventListener('keydown', (e: KeyboardEvent) => {
      if (dropdown.style.display === 'none') return
      if (['ArrowDown', 'ArrowUp', 'Enter', 'Escape'].includes(e.key)) e.preventDefault()
      switch (e.key) {
        case 'ArrowDown': {
          if (filtered.length === 0) return
          selectedIndex = selectedIndex < filtered.length - 1 ? selectedIndex + 1 : 0
          updateActive()

          break
        }
        case 'ArrowUp': {
          if (filtered.length === 0) return
          selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : filtered.length - 1
          updateActive()

          break
        }
        case 'Enter': {
          if (selectedIndex >= 0) commitSelection(selectedIndex)

          break
        }
        case 'Escape': {
          hide()

          break
        }
        // No default
      }
    })

    // 跟随窗口变化定位
    ;(view as Window).addEventListener('resize', position)
    ;(view as Window).addEventListener('scroll', position, true)

    doc.body.append(dropdown)
  }

  /**
   * 填充凭据
   */
  private async fillCredentials(loginForm: LoginForm, credential: Credential): Promise<void> {
    if (!loginForm.usernameField || !loginForm.passwordField) return

    // 填充用户名
    loginForm.usernameField.value = credential.username
    loginForm.usernameField.dispatchEvent(new Event('input', { bubbles: true }))
    loginForm.usernameField.dispatchEvent(new Event('change', { bubbles: true }))

    // 填充密码
    loginForm.passwordField.value = credential.password
    loginForm.passwordField.dispatchEvent(new Event('input', { bubbles: true }))
    loginForm.passwordField.dispatchEvent(new Event('change', { bubbles: true }))

    // 更新最后使用时间
    try {
      await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.UPDATE_LAST_USED, credential._id)
    } catch (error) {
      console.error('更新最后使用时间失败:', error)
    }

    // 同步填充到子 iframe（由 paf-inject.js 处理实际写入）
    this.broadcastFillToFrames(credential)
  }

  /** 广播填充指令到子 iframe（由 paf-inject.js 接收并填充） */
  private broadcastFillToFrames(credential: Credential): void {
    try {
      const payload = { username: credential.username, password: credential.password }
      const iframes = document.querySelectorAll('iframe')
      iframes.forEach((f: HTMLIFrameElement) => {
        try {
          f.contentWindow?.postMessage({ __paf: true, type: 'paf:fill', payload }, '*')
        } catch {
          /* ignore */
        }
      })
    } catch {
      /* ignore */
    }
  }

  /**
   * 监听来自子 iframe 的“请求填充”消息，并按子 iframe 的来源域名选择凭据后，定向回发填充指令
   */
  private setupFrameFillRequestListener(): void {
    // eslint-disable-next-line complexity
    const handler = async (e: MessageEvent) => {
      try {
        const d: any = e && (e as any).data
        if (!d || d.__paf !== true) return
        switch (d.type) {
          case 'paf:request-fill': {
            const sourceWin = (e as any).source as Window | null
            const origin: string = (e as any).origin || '*'

            // 检查设置（仅在自动填充开启时响应）
            const settings = await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.GET_SETTINGS)
            const enabled = settings?.success && settings?.data?.auto_fill_enabled
            if (!enabled) return

            // 用子 iframe 的 origin 来获取对应域名的凭据
            let host = ''
            try {
              host = new URL(origin).hostname
            } catch {
              host = window.location.hostname
            }
            const credentials = await this.getCredentialsForDomain(host)
            if (!credentials || credentials.length === 0) return

            if (credentials.length === 1) {
              const cred = credentials[0]!
              sourceWin?.postMessage(
                {
                  __paf: true,
                  type: 'paf:fill',
                  payload: { username: cred.username, password: cred.password }
                },
                origin || '*'
              )
              return
            }

            // 多个凭据：在主文档弹出选择面板，用户选择后定向回发
            this.showIframeCredentialPicker(sourceWin, origin, credentials)
            break
          }
          case 'paf:save': {
            try {
              const settingsRes = await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.GET_SETTINGS)
              if (!settingsRes?.success || !settingsRes.data?.enabled) return

              const p = d.payload || {}
              const pageUrl: string = p.pageUrl || ''
              const hostname = new URL(window.location.href).hostname
              let site_name = `${hostname}-${document.title}`
              let domain = ''
              try {
                domain = new URL(pageUrl || p.origin).hostname
              } catch {
                domain = ''
              }
              if (hostname !== domain) site_name = `${hostname}(${domain})-${document.title}`

              const cred = {
                credentialId: '',
                domain: domain || hostname,
                username: p.username || '',
                password: p.password || '',
                site_name,
                favicon_url: p.favicon_url || this.getFaviconUrl(),
                url: pageUrl
              }
              const result = await ipcRenderer.invoke(
                PASSWORD_MANAGER_CHANNELS.GET_DECRYPTED_CREDENTIALS,
                {
                  domain,
                  auto_fill_enabled: true,
                  sortBy: 'last_used_at',
                  sortOrder: 'desc'
                }
              )
              if (result.success && result.data) {
                const existingCredential = result.data.find(
                  (cred: Credential) => cred.username === p.username
                )
                cred.credentialId = existingCredential?._id || ''
              }

              await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.SHOW_SAVE_PROMPT, cred)
            } catch {
              /* empty */
            }

            break
          }
          // No default
        }
      } catch (error) {
        console.error('处理子 frame 请求填充消息失败:', error)
      }
    }
    window.addEventListener('message', handler, true)
  }

  /**
   * 在主文档展示“为子 iframe 选择账号”的选择面板
   */
  private showIframeCredentialPicker(
    sourceWin: Window | null,
    origin: string,
    credentials: Credential[]
  ): void {
    try {
      const doc = document
      // 唯一面板：重复请求时替换
      const existed = doc.querySelector('#paf-iframe-picker')
      existed?.remove()

      const host = (() => {
        try {
          return new URL(origin).hostname
        } catch {
          return location.hostname
        }
      })()

      const panel = doc.createElement('div')
      panel.id = 'paf-iframe-picker'
      panel.style.cssText = `
        position: fixed; top: 16px; right: 16px; width: 320px;
        background: #fff; border: 1px solid #e0e0e0; border-radius: 10px;
        box-shadow: 0 10px 30px rgba(0,0,0,0.18); z-index: 2147483647;
        font-family: -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;
        color: #222; overflow: hidden;
      `

      const header = doc.createElement('div')
      header.style.cssText =
        'padding:12px 14px;font-weight:600;border-bottom:1px solid #eee;display:flex;align-items:center;justify-content:space-between;'
      header.textContent = `选择要填充的账号（${host}）`

      const closeBtn = doc.createElement('button')
      closeBtn.textContent = '×'
      closeBtn.style.cssText =
        'background:none;border:none;font-size:18px;line-height:1;cursor:pointer;color:#666;'
      closeBtn.addEventListener('click', () => panel.remove())
      header.append(closeBtn)

      const list = doc.createElement('div')
      list.style.cssText = 'max-height:360px;overflow:auto;padding:6px 0;'

      const send = (cred: Credential) => {
        sourceWin?.postMessage(
          {
            __paf: true,
            type: 'paf:fill',
            payload: { username: cred.username, password: cred.password }
          },
          origin || '*'
        )

        panel.remove()
      }

      for (const cred of credentials) {
        const item = doc.createElement('div')
        item.style.cssText =
          'padding:10px 14px;cursor:pointer;display:flex;flex-direction:column;gap:2px;'
        item.addEventListener('mouseenter', () => (item.style.background = '#f5f7ff'))
        item.addEventListener('mouseleave', () => (item.style.background = 'transparent'))
        item.addEventListener('click', () => send(cred))

        const line1 = doc.createElement('div')
        line1.style.cssText =
          'font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;'
        line1.textContent = cred.username
        const line2 = doc.createElement('div')
        line2.style.cssText =
          'font-size:12px;color:#666;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;'
        line2.textContent = cred.site_name || cred.domain

        item.append(line1, line2)
        list.append(item)
      }

      panel.append(header, list)
      doc.body.append(panel)
    } catch {
      /* empty */
    }
  }

  /**


   * 设置表单提交监听器，用于保存密码
   */
  private setupFormSubmissionListener(): void {
    document.addEventListener('submit', async event => {
      // event.preventDefault()
      // event.stopPropagation()
      const form = event.target as HTMLFormElement
      if (!form || form.tagName !== 'FORM') return

      const loginForm = this.analyzeForm(form, true)

      if (!loginForm.usernameField || !loginForm.passwordField) {
        return
      }

      const username = loginForm.usernameField.value.trim()
      const password = loginForm.passwordField.value.trim()

      if (!username || !password) {
        return
      }
      // 检查是否已存在该凭据（查询所有凭据，不仅限于启用自动填充的）
      const domain = window.location.hostname
      const existingCredentials = await this.getAllCredentialsForDomain(domain)
      const existingCredential = existingCredentials.find(cred => cred.username === username)
      if (password === existingCredential?.password) return
      await this.showSavePasswordPromptViaIPC(domain, username, password, existingCredential?._id)
    })
  }

  /**
   * 通过IPC发送保存密码提示到主进程
   */
  private async showSavePasswordPromptViaIPC(
    domain: string,
    username: string,
    password: string,
    credentialId?: string
  ): Promise<void> {
    try {
      // 检查设置是否允许显示保存提示
      const enabled = await this.checkSavePromptEnabled()
      if (!enabled) return
      this.lastSavePromptAt = Date.now()
      await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.SHOW_SAVE_PROMPT, {
        credentialId,
        domain,
        username,
        password,
        site_name: `${domain}-${document.title}`,
        favicon_url: this.getFaviconUrl(),
        url: window.location.href
      })
    } catch (error) {
      console.error('发送保存密码提示失败:', error)
    }
  }

  /**
   * 检查是否启用保存提示
   */
  private async checkSavePromptEnabled(): Promise<boolean> {
    try {
      const result = await ipcRenderer.invoke(PASSWORD_MANAGER_CHANNELS.GET_SETTINGS)

      return result.success && result.data?.save_prompt_enabled
    } catch (error) {
      console.error('检查保存提示设置失败:', error)
      return false
    }
  }

  /**
   * 给顶层文档打 fetch/XHR 网络钩子，用于捕获无 FORM/SPA 登录的保存提示
   */
  private patchNetworkHooks(): void {
    if (this.networkPatched) return
    this.networkPatched = true

    // 1) 预加载侧监听来自主世界的网络活动事件
    try {
      document.addEventListener(
        'paf:net-activity',
        () => {
          if (Date.now() - this.lastPwAt < 8000) void this.maybePromptSaveFromNetwork()
        },
        { capture: true }
      )
    } catch {
      /* empty */
    }

    // 2) 优先通过 webFrame.executeJavaScript 注入到主世界（绕过 CSP）；失败则降级为 <script> 注入
    try {
      let injected = false
      try {
        const electron = require('electron') as any
        const wf = electron?.webFrame
        if (wf && typeof wf.executeJavaScript === 'function') {
          const code = `(() => {
            try {
              if (window.__pafNetPatched) return; window.__pafNetPatched = true;
              const dispatch = () => {
                try { document.dispatchEvent(new CustomEvent('paf:net-activity')) } catch {}
              };
              // fetch
              try {
                const origFetch = window.fetch;
                if (typeof origFetch === 'function') {
                  window.fetch = function (...args) {
                    const p = origFetch.apply(this, args);
                    try { Promise.resolve(p).finally(dispatch) } catch {}
                    return p;
                  };
                }
              } catch {}
              // XHR
              try {
                const XHR = window.XMLHttpRequest;
                if (XHR && XHR.prototype) {
                  const _open = XHR.prototype.open;
                  const _send = XHR.prototype.send;
                  if (_open && _send) {
                    XHR.prototype.open = function(method, url) {
                      try { this.__pafMethod = method; this.__pafUrl = url } catch {}
                      return _open.apply(this, arguments);
                    };
                    XHR.prototype.send = function() {
                      try { this.addEventListener('loadend', dispatch) } catch {}
                      return _send.apply(this, arguments);
                    };
                  }
                }
              } catch {}
              // beforeunload 兜底
              try { window.addEventListener('beforeunload', () => { dispatch() }, { capture: true }) } catch {}
            } catch {}
          })();`
          void wf.executeJavaScript(code, false)
          injected = true
        }
      } catch {
        /* empty */
      }
      if (!injected) {
        const script = document.createElement('script')
        script.id = '__paf_net_hooks__'
        script.textContent = `(() => {
          try {
            if (window.__pafNetPatched) return; window.__pafNetPatched = true
            const dispatch = () => {
              try { document.dispatchEvent(new CustomEvent('paf:net-activity')) } catch {}
            }
            // fetch
            try {
              const origFetch = window.fetch
              if (typeof origFetch === 'function') {
                window.fetch = function (...args) {
                  const p = origFetch.apply(this, args)
                  try { Promise.resolve(p).finally(dispatch) } catch {}
                  return p
                }
              }
            } catch {}
            // XHR
            try {
              const XHR = window.XMLHttpRequest
              if (XHR && XHR.prototype) {
                const _open = XHR.prototype.open
                const _send = XHR.prototype.send
                if (_open && _send) {
                  XHR.prototype.open = function(method, url) {
                    try { this.__pafMethod = method; this.__pafUrl = url } catch {}
                    return _open.apply(this, arguments)
                  }
                  XHR.prototype.send = function() {
                    try { this.addEventListener('loadend', dispatch) } catch {}
                    return _send.apply(this, arguments)
                  }
                }
              }
            } catch {}
            // beforeunload 兜底
            try { window.addEventListener('beforeunload', () => { dispatch() }, { capture: true }) } catch {}
          } catch {}
        })();`
        ;(document.head || document.documentElement).append(script)
        script.remove()
      }
    } catch {
      /* empty */
    }
  }

  /**
   * 在网络请求结束后，基于最近一次密码输入，尝试触发保存提示
   */
  private async maybePromptSaveFromNetwork(): Promise<void> {
    const now = Date.now()
    if (now - this.lastPwAt > 8000) return // 超过时间窗口
    if (now - this.lastSavePromptAt < 2500) return // 去重

    // 尝试定位密码输入
    const doc = document
    let pw: HTMLInputElement | null = null

    if (
      this.isUsableInput(this.lastPasswordField) &&
      (this.lastPasswordField!.value || '').trim()
    ) {
      pw = this.lastPasswordField!
    } else {
      const active = doc.activeElement as any
      if (
        active &&
        active.tagName === 'INPUT' &&
        this.isPwInput(active) &&
        (active.value || '').trim()
      ) {
        pw = active as HTMLInputElement
      }
    }
    if (!pw) {
      const cand = Array.from(doc.querySelectorAll('input')).find(
        el =>
          this.isPwInput(el as HTMLInputElement) &&
          this.isUsableInput(el as HTMLInputElement) &&
          ((el as HTMLInputElement).value || '').trim()
      ) as HTMLInputElement | undefined
      pw = cand || null
    }
    if (!pw) return

    const password = (pw.value || '').trim()
    if (!password) return

    // 定位用户名
    let username = ''
    const u = this.findNearestUsernameField(pw)
    if (this.isUsableInput(u) && (u!.value || '').trim()) {
      username = u!.value.trim()
    } else if (
      this.isUsableInput(this.lastUsernameField) &&
      (this.lastUsernameField!.value || '').trim()
    ) {
      username = this.lastUsernameField!.value.trim()
    } else {
      const anyU = Array.from(doc.querySelectorAll('input')).find(
        el =>
          this.isUsernameField(el as HTMLInputElement) &&
          this.isUsableInput(el as HTMLInputElement) &&
          ((el as HTMLInputElement).value || '').trim()
      ) as HTMLInputElement | undefined
      if (anyU) username = anyU.value.trim()
    }
    if (!username) return

    const domain = window.location.hostname
    try {
      const existing = await this.getAllCredentialsForDomain(domain)
      const existingCred = existing.find(c => c.username === username)
      this.lastSavePromptAt = now

      if (password === existingCred?.password) return
      await this.showSavePasswordPromptViaIPC(domain, username, password, existingCred?._id)
    } catch {
      /* empty */
    }
  }

  /**
   * 全局交互监听（覆盖无表单/多步登录/动态页面）
   */
  private setupGlobalInteractionListeners(): void {
    // 记录用户名/密码输入
    document.addEventListener(
      'input',
      e => {
        const t = e.target as HTMLInputElement
        if (!t || t.tagName !== 'INPUT') return
        if (this.isUsernameField(t)) {
          this.lastUsernameField = t
        } else if (this.isPwInput(t)) {
          this.lastPasswordField = t
          this.lastPwAt = Date.now()
        }
      },
      true
    )

    // 聚焦到用户名/密码时，尝试绑定自动填充
    document.addEventListener(
      'focusin',
      e => {
        const t = e.target as HTMLInputElement
        if (!t || t.tagName !== 'INPUT') return
        if (this.isUsernameField(t)) {
          const pw = this.findNearestPasswordField(t)
          if (pw)
            this.setupAutoFill({
              form: t.form || null,
              usernameField: t,
              passwordField: pw,
              submitButton: null
            })
        } else if (this.isPwInput(t)) {
          const u = this.findNearestUsernameField(t)
          if (u)
            this.setupAutoFill({
              form: t.form || null,
              usernameField: u,
              passwordField: t,
              submitButton: null
            })
        }
      },
      true
    )
  }

  private isUsableInput(i: HTMLInputElement | null): i is HTMLInputElement {
    return !!(
      i &&
      !i.disabled &&
      !i.readOnly &&
      i.type !== 'hidden' &&
      (i.offsetParent !== null || i.getClientRects().length > 0)
    )
  }

  /**
   * 在同 form、同容器（向上最多3层）、全局范围内按先后顺序查找满足条件的可用输入
   */
  private findNearbyInput(
    anchor: HTMLElement,
    predicate: (i: HTMLInputElement) => boolean,
    includeGlobal = true
  ): HTMLInputElement | null {
    const inForm =
      (anchor as HTMLInputElement).form || (anchor.closest('form') as HTMLFormElement | null)
    if (inForm) {
      const cand = Array.from(inForm.querySelectorAll('input')) as HTMLInputElement[]
      const hit = cand.find(inp => predicate(inp) && this.isUsableInput(inp)) || null
      if (hit) return hit
    }
    let p: HTMLElement | null = anchor.parentElement
    for (let depth = 0; p && depth < 3; depth++, p = p.parentElement) {
      const inputs = Array.from(p.querySelectorAll('input')) as HTMLInputElement[]
      const hit = inputs.find(inp => predicate(inp) && this.isUsableInput(inp)) || null
      if (hit) return hit
    }
    if (includeGlobal) {
      const globalList = Array.from(
        (anchor.ownerDocument || document).querySelectorAll('input')
      ) as HTMLInputElement[]
      return globalList.find(inp => predicate(inp) && this.isUsableInput(inp)) || null
    }
    return null
  }

  /** 查找与锚点邻近的密码框（优先同 form，其次同容器，最后全局可见第一个） */
  private findNearestPasswordField(anchor: HTMLElement): HTMLInputElement | null {
    return this.findNearbyInput(anchor, inp => this.isPwInput(inp), true)
  }

  /** 查找与密码框邻近的用户名框 */
  private findNearestUsernameField(pw: HTMLInputElement): HTMLInputElement | null {
    if (pw.form) {
      // 复用已有表单分析逻辑
      const info = this.analyzeForm(pw.form)
      if (this.isUsableInput(info.usernameField)) return info.usernameField
    }
    // 同容器/上层几级内寻找（不包含全局）
    {
      const near = this.findNearbyInput(pw, inp => this.isUsernameField(inp), false)
      if (near) return near
    }
    // 兜底顺序：最近一次用户名输入 -> 全局
    if (this.isUsableInput(this.lastUsernameField)) return this.lastUsernameField!
    return this.findNearbyInput(pw, inp => this.isUsernameField(inp), true)
  }

  /**
   * 获取网站图标URL
   */
  private getFaviconUrl(): string {
    const favicon = document.querySelector('link[rel="icon"]') as HTMLLinkElement
    return favicon?.href || `${window.location.origin}/favicon.ico`
  }

  /**
   * 销毁管理器
   */
  destroy(): void {
    if (this.observer) {
      this.observer.disconnect()
      this.observer = null
    }

    // 移除现有提示/下拉
    const prompts = document.querySelectorAll(
      '.password-autofill-prompt, .password-save-prompt, .password-autofill-dropdown'
    )
    for (const prompt of prompts) prompt.remove()
  }
}

// 创建全局实例
const passwordAutoFillManager = new PasswordAutoFillManager()

/**
 * 初始化密码自动填充功能
 */
export function setupPasswordAutoFill(): void {
  if (document.readyState === 'loading') {
    window.addEventListener('DOMContentLoaded', () => passwordAutoFillManager.init(), {
      once: true
    })
  } else {
    passwordAutoFillManager.init()
  }
}
